package git.soulbgm.edu.piesat;

import git.soulbgm.edu.piesat.pojo.EduConfig;
import git.soulbgm.edu.piesat.pojo.IntegralInfo;
import git.soulbgm.edu.piesat.pojo.KnowledgeType;
import git.soulbgm.edu.piesat.pojo.LearningProgress;
import git.soulbgm.utils.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * 程序入口
 *
 * @author SoulBGM
 * @date 2021/03/31
 */
public class EduPiesatApplication {

    private static WebDriver driver;
    private static EduConfig eduConfig;
    private static String baseUrl = "http://edu.piesat.cn/";
    private static String integralUrl = baseUrl + "study/mygradeintegral.htm";
    private static LogUtil log = new LogUtil(EduPiesatApplication.class);
    /**
     * 因为课件在一定时间如果没有滚动会弹框提醒 所有需要一定时间进行滚动一下
     */
    private static int refreshPeriod = 10;
    private static String[][] urls = {
            {KnowledgeType.KNOWLEDGE_TYPE_DOCUMENT, "http://edu.piesat.cn/kng/knowledgecatalogsearch.htm?t=DocumentKnowledge&mode=null&pi="},
            {KnowledgeType.KNOWLEDGE_TYPE_VIDEO, "http://edu.piesat.cn/kng/knowledgecatalogsearch.htm?t=VideoKnowledge&h=menu&pi="},
            {KnowledgeType.KNOWLEDGE_TYPE_PACKAGE, "http://edu.piesat.cn/kng/knowledgecatalogsearch.htm?t=CourseKnowledge&h=menu&pi="}
    };

    static {
        try {
            detectionEnvironment();
        } catch (IOException e) {
            log.error("环境检测出现问题", e);
        }
    }

    /**
     * 得到总页面
     * 获取总页数
     *
     * @param url url
     * @return int
     */
    public static int getTotalPage(String url) {
        driver.get(url);
        Document document = getDocument(driver);
        String text = document.getElementsByClass("pagenumber").last().text();
        return Integer.parseInt(text);
    }

    /**
     * 获取页面的url
     *
     * @return {@link List<String>}
     */
    private static List<LearningProgress> getPageUrl() {
        List<LearningProgress> list = new ArrayList<>();
        for (String[] urlInfo : urls) {
            String knowledgeType = urlInfo[0];
            String url = urlInfo[1];
            int total = getTotalPage(url);
            for (int i = 1; i <= total; i++) {
                String pageUrl = url + i;
                driver.get(pageUrl);
                Document document = getDocument(driver);
                Elements li = document.getElementsByClass("el-kng-img-list clearfix").last().getElementsByTag("li");
                for (Element element : li) {
                    LearningProgress learningProgress = new LearningProgress(knowledgeType);
                    Elements userCompleteElements = element.getElementsByAttributeValue("data-localize", "sty_lbl_usercomplete");
                    if (userCompleteElements.size() > 0) {
                        String content = userCompleteElements.first().html();
                        if ("已完成".equals(content)) {
                            learningProgress.setState(true);
                        }
                    }
                    String attr = element.getElementsByClass("el-placehold el-placehold-default").first()
                            .getElementsByTag("img").last().attr("onclick");
                    String suffix = attr.substring(attr.indexOf("'") + 1, attr.lastIndexOf("'"));
                    String u = baseUrl + suffix;
                    learningProgress.setUrl(u);
                    list.add(learningProgress);
                }
            }
        }
        return list;
    }

    /**
     * 转换秒
     *
     * @param timeStr 时间字符串
     * @return long
     */
    public static int convertSecond(String timeStr) {
        SimpleDateFormat sdf = new SimpleDateFormat();
        if (!timeStr.contains("小时") && !timeStr.contains("分钟") && !timeStr.contains("秒")) {
            return 0;
        }

        if (timeStr.contains("小时") && timeStr.contains("分钟") && timeStr.contains("秒")) {
            sdf.applyLocalizedPattern("hh小时mm分钟ss秒");
        } else if (timeStr.contains("小时") && timeStr.contains("分钟")) {
            sdf.applyLocalizedPattern("hh小时mm分钟");
        } else if (timeStr.contains("小时") && timeStr.contains("秒")) {
            sdf.applyLocalizedPattern("hh小时ss秒");
        } else if (timeStr.contains("小时")) {
            sdf.applyLocalizedPattern("hh小时");
        } else if (timeStr.contains("分钟") && timeStr.contains("秒")) {
            sdf.applyLocalizedPattern("mm分钟ss秒");
        } else if (timeStr.contains("分钟")) {
            sdf.applyLocalizedPattern("mm分钟");
        } else if (timeStr.contains("秒")) {
            sdf.applyLocalizedPattern("ss秒");
        }
        Calendar calendar = Calendar.getInstance();
        try {
            calendar.setTime(sdf.parse(timeStr));
        } catch (ParseException e) {
            log.error("获取课件需观看时间出现异常,异常内容如下:\n", e);
            sendEmail("获取课件需观看时间出现异常", e.getMessage());
            //e.printStackTrace();
        }
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        int second = calendar.get(Calendar.SECOND);

        return (hour * 60 * 60) + (minute * 60) + second;
    }

    /**
     * 初始化数据
     */
    private static void initData() {
        List<LearningProgress> list = getPageUrl();
        JDBCUtil.insertList(list, true);
    }

    /**
     * 获取学习进度信息
     *
     * @return {@link LearningProgress}
     */
    private static int getLearningDuration(Document document) {
        String timeStr = document.getElementById("spanLeavTimes").text();
        String completionRate = document.getElementById("ScheduleText").text();
        int second = convertSecond(timeStr);
        boolean state = false;
        if ("100%".equals(completionRate)) {
            state = true;
        }
        if (!state && second == 0) {
            sleep(15 * 1000);
            document = getDocument(driver);
            timeStr = document.getElementById("spanLeavTimes").text();
            second = convertSecond(timeStr);
        }
        return state ? -1 : second == 0 ? 15 : second;
        //return state && second == 0 ? -1 : second;
    }

    /**
     * 得到文件
     *
     * @param driver 司机
     * @return {@link Document}
     */
    private static Document getDocument(WebDriver driver) {
        String htmlContent = driver.getPageSource();
        return Jsoup.parse(htmlContent);
    }

    private static void clearData() {
        JDBCUtil.executeUpdate("delete from learning_progress", null);
    }

    /**
     * 睡眠
     *
     * @param millis 毫秒
     */
    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获得实际学习课件的数量
     *
     * @param list 列表
     * @return int
     */
    private static int startLearning(List<LearningProgress> list) {
        if (list == null) {
            return 0;
        }
        int learningCount = 0;
        for (LearningProgress progress : list) {
            if (!progress.getState()) {
                String url = progress.getUrl();
                Document document = getUrl(url, progress.getKnowledgeType());
                boolean flag = false;
                if (KnowledgeType.KNOWLEDGE_TYPE_PACKAGE.equals(progress.getKnowledgeType()) && !url.contains("ebook")) {
                    flag = learningPackage(document);
                } else {
                    flag = learningDocumentAndVideo(document);
                }
                if (flag) {
                    learningCount++;
                }
                updateLearningState(progress);
            }
            if (checkIntegral() || learningCount > 19) {
                break;
            }
        }
        return learningCount;
    }

    private static boolean learningPackage(Document document) {
        //picnostart 还没有开始
        //picstudying 未完成
        //piccomplected 完成
        Elements noStartElements = document.getElementsByClass("picnostart");
        Elements studyingElements = document.getElementsByClass("picstudying");
        if (noStartElements.size() == 0 && studyingElements.size() == 0) {
            // 说明没有 未完成的课件了
            return false;
        } else {
            Elements elements = document.getElementsByClass("normalrow");
            for (Element element : elements) {
                Elements complectedElements = element.getElementsByClass("piccomplected");
                noStartElements = element.getElementsByClass("picnostart");
                studyingElements = element.getElementsByClass("picstudying");
                if (complectedElements == null || complectedElements.size() == 0) {
                    String href = element.getElementsByClass("text-color6").last().attr("href");
                    String url = href.substring(href.indexOf("'") + 1, href.indexOf("',"));
                    url = baseUrl + url;
                    String knowledgeType = KnowledgeType.KNOWLEDGE_TYPE_DOCUMENT;
                    if (href.contains("VideoKnowledge")) {
                        knowledgeType = KnowledgeType.KNOWLEDGE_TYPE_VIDEO;
                    }
                    Document childDocument = getUrl(url, knowledgeType);
                    learningDocumentAndVideo(childDocument);
                }
            }
            return true;
        }
    }

    /**
     * 学习文档和视频
     *
     * @param document 文档
     * @return int
     */
    private static boolean learningDocumentAndVideo(Document document) {
        int duration = getLearningDuration(document);
        if (duration == -1) {
            // 以前看过
            return false;
        } else {
            String name = document.getElementById("lblTitle").text();
            log.info("开始学习{}, 学习时长:{}秒", name, duration);
            while (true) {
                sleep(refreshPeriod * 1000);
                document = getDocument(driver);
                getClickButton(document);
                int second = getLearningDuration(document);
                if (second == -1) {
                    break;
                }
            }
            /*document = getDocument(driver);
            duration = getLearningDuration(document);
            if (duration == -1) {
                updateLearningState(progress);
            }*/
            return true;
        }
    }

    /**
     * 获取继续学习按钮
     *
     * @return {@link LearningProgress}
     */
    private static void getClickButton(Document document) {
        Element dvWarningView1 = document.getElementById("dvWarningView");
        if (dvWarningView1 == null) {
            return;
        }
        WebElement dvWarningView = driver.findElement(By.id("dvWarningView"));
        dvWarningView.findElement(By.className("btnok")).click();
    }

    /**
     * 获取url
     *
     * @param url           url
     * @param knowledgeType 知识类型
     * @return {@link Document}
     */
    private static Document getUrl(String url, String knowledgeType) {
        driver.get(url);
        Document document = getDocument(driver);
        if (KnowledgeType.KNOWLEDGE_TYPE_VIDEO.equals(knowledgeType)) {
            try {
                // 点击视频界面
                Element v = document.getElementById("vjs_video_3_html5_api");
                WebElement video = null;
                if (v != null) {
                    video = driver.findElement(By.id("vjs_video_3_html5_api"));
                } else {
                    video = driver.findElements(By.className("jw-icon-playback")).get(0);
                }
                video.click();
            } catch (Exception e) {
                log.error("跳转课件地址并获取课件信息时出现异常,异常内容如下:\n", e);
                sendEmail("跳转课件地址并获取课件信息时出现异常", e.getMessage());
                //e.printStackTrace();
                //getUrl(progress);
            }
        }
        return document;
    }

    /**
     * 检查积分
     */
    public static boolean checkIntegral() {
        try {
            driver.get(integralUrl);
            List<IntegralInfo> firstPageIntegralInfos = getIntegralInfos(getDocument(driver));
            if (firstPageIntegralInfos.size() == 0) {
                return false;
            }
            IntegralInfo integralInfo = firstPageIntegralInfos.get(0);
            if (integralInfo.getIntegral() == 0) {
                log.error("今日观看的课件已达到最高限制，将停止观看");
                driver.close();
                System.exit(0);
            }
            int totalIntegral = getIntegral(firstPageIntegralInfos);
            String attribute = driver.findElements(By.className("pagetext")).get(1).getAttribute("class");
            if (!attribute.contains("disable")) {
                driver.findElements(By.className("pagetext")).get(1).click();
                List<IntegralInfo> secondPageIntegralInfos = null;
                while (true) {
                    secondPageIntegralInfos = getIntegralInfos(getDocument(driver));
                    if (secondPageIntegralInfos.size() > 0 && firstPageIntegralInfos.size() > 0) {
                        if (secondPageIntegralInfos.get(0).getTitle().equals(integralInfo.getTitle())) {
                            sleep(500);
                            continue;
                        }
                    }
                    break;
                }
                totalIntegral += getIntegral(secondPageIntegralInfos);
            }
            log.info("本次积分检查，积分值:{}", totalIntegral);
            if (totalIntegral >= 1910) {
                return true;
            }
            /*driver.findElement(By.id("oscomboxtitle_pageNav")).click();
            List<WebElement> elements = driver.findElement(By.id("comboxlist_pageNav")).findElements(By.className("listitem"));
            elements.get(4).click();*/
        } catch (Exception e) {
            log.error("检查积分是否达到今日最多获取量时出现异常,异常内容如下:\n", e);
            sendEmail("检查积分是否达到今日最多获取量时出现异常", e.getMessage());
            //return checkIntegral();
        }
        return false;
    }

    /**
     * 获得积分
     *
     * @param integralInfos 积分信息
     * @return int
     */
    private static int getIntegral(List<IntegralInfo> integralInfos) {
        int totalIntegral = 0;
        for (IntegralInfo info : integralInfos) {
            totalIntegral += info.getIntegral();
        }
        return totalIntegral;
    }

    /**
     * 获得积分信息
     *
     * @param document 文档
     * @return {@link List<IntegralInfo>}
     */
    private static List<IntegralInfo> getIntegralInfos(Document document) {
        Element tableElement = document.getElementsByClass("table mt10 mb10").first();
        Elements trElements = tableElement.getElementsByTag("tbody").first().getElementsByTag("tr");
        return getIntegralInfos(trElements);
    }

    /**
     * 获得积分信息
     *
     * @param trElements tr元素
     * @return {@link List<IntegralInfo>}
     */
    private static List<IntegralInfo> getIntegralInfos(Elements trElements) {
        int index = 0;
        List<IntegralInfo> integralInfos = new ArrayList<>(trElements.size());
        for (Element tr : trElements) {
            index++;
            if (index == 1) {
                continue;
            }
            String title = tr.getElementsByClass("ellipsis").attr("title");
            String integral = tr.getElementsByClass("text-center").last().getElementsByClass("text-info font-bold").last().text();
            String date = tr.getElementsByTag("td").last().text().trim();
            String ymd = DateUtil.format(DateUtil.getStartCalendar(DateUtil.getCalendar(), DateUtil.DAY).getTime(), DateUtil.FORMAT_YMD);
            if (ymd != null && !date.startsWith(ymd)) {
                continue;
            }
            IntegralInfo info = new IntegralInfo();
            info.setTitle(title);
            info.setIntegral(Integer.parseInt(integral));
            info.setDate(DateUtil.parse(date, DateUtil.FORMAT_YMD_HM));
            integralInfos.add(info);
            /*long startTime = DateUtil.getStartCalendar(DateUtil.getCalendar(), DateUtil.DAY).getTimeInMillis();

            Calendar endCalendar = DateUtil.offsetDate(DateUtil.getCalendar(startTime), DateUtil.DAY, 1);
            DateUtil.offsetDate(endCalendar, DateUtil.MILLISECOND, -1);

            long time = info.getDate().getTime();
            if (time >= startTime && time <= endCalendar.getTimeInMillis()) {
                integralInfos.add(info);
            }*/
        }
        return integralInfos;
    }

    /**
     * 更新学习状态
     *
     * @param progress 学习进度信息
     */
    private static void updateLearningState(LearningProgress progress) {
        progress.setState(true);
        progress.setUpdateTime(System.currentTimeMillis());
        JDBCUtil.update(progress, "id");
    }

    /**
     * 判断运行时是否是以jar运行
     *
     * @return boolean
     */
    private static boolean isJar(String path) {
        File file = new File(path);
        return file.isFile();
    }

    /**
     * 创建数据库和表
     *
     * @param sql    sql
     * @param dbPath 数据库路径
     * @throws IOException ioexception
     */
    private static void createDatabaseAndTable(String sql, String dbPath) throws IOException {
        File dbFile = new File(dbPath);
        if (!dbFile.exists()) {
            dbFile.createNewFile();
            JDBCUtil.executeBatch(sql);
        }
    }

    /**
     * 检测环境
     */
    private static void detectionEnvironment() throws IOException {
        String path = EduPiesatApplication.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        String sql = PropertiesUtil.getConfigurationFileContext("sql/edu.sql");
        String configFilePath = "config.properties";
        if (isJar(path)) {
            path = path.substring(0, path.lastIndexOf(PropertiesUtil.LEFT_SLANTING_BAR));
            configFilePath = path + File.separator + "config.properties";
            String dbPath = path + File.separator + "edu.db";
            JDBCUtil.setUrl("jdbc:sqlite:" + dbPath);
            createDatabaseAndTable(sql, dbPath);
        } else {
            String userDir = System.getProperty("user.dir");
            String dbFolder = userDir + File.separator + "db";
            File dbFolderFile = new File(dbFolder);
            if (!dbFolderFile.exists()) {
                dbFolderFile.mkdir();
            }
            String dbPath = dbFolder + File.separator + "edu.db";
            JDBCUtil.setUrl("jdbc:sqlite:" + dbPath);
            createDatabaseAndTable(sql, dbPath);
        }

        eduConfig = new EduConfig();
        Map<String, String> config = PropertiesUtil.getPrefixProperties(configFilePath, "config", "#");
        eduConfig.setUsername(config.get("username"));
        eduConfig.setPassword(config.get("password"));
        eduConfig.setKnowledgeTypes(config.get("learningKnowledgeTypes").split(","));
        eduConfig.setChromeDriverPath(config.get("chromeDriverPath"));
        eduConfig.setSenderMailboxAddress(config.get("senderMailboxAddress"));
        eduConfig.setMailboxUsername(config.get("mailboxUsername"));
        eduConfig.setMailboxPassword(config.get("mailboxPassword"));
        eduConfig.setRecipientMailboxAddress(config.get("recipientMailboxAddress"));
        System.setProperty("webdriver.chrome.driver", eduConfig.getChromeDriverPath());
        if (eduConfig.getKnowledgeTypes() == null || eduConfig.getKnowledgeTypes().length == 0) {
            eduConfig.setKnowledgeTypes(new String[]{KnowledgeType.KNOWLEDGE_TYPE_DOCUMENT, KnowledgeType.KNOWLEDGE_TYPE_VIDEO, KnowledgeType.KNOWLEDGE_TYPE_PACKAGE});
        }
    }

    private static void sendEmail(String subject, String text) {
        if (StringUtil.isNotEmpty(eduConfig.getSenderMailboxAddress(), eduConfig.getMailboxUsername(), eduConfig.getMailboxPassword(), eduConfig.getRecipientMailboxAddress())) {
            EmailUtil.sendEmail(eduConfig.getSenderMailboxAddress(),
                    eduConfig.getMailboxUsername(),
                    eduConfig.getMailboxPassword(),
                    eduConfig.getRecipientMailboxAddress(),
                    subject,
                    text);
        }
    }

    public static void main(String[] args) throws IOException {
        detectionEnvironment();
        ChromeOptions chromeOptions = new ChromeOptions();
        chromeOptions.addArguments("--mute-audio");
        chromeOptions.addArguments("--hide-scrollbars");
        driver = new ChromeDriver(chromeOptions);
        //driver.manage().window().maximize();
        driver.get(baseUrl);
        driver.findElement(By.id("txtUserName2")).sendKeys(eduConfig.getUsername());
        driver.findElement(By.id("txtPassword2")).sendKeys(eduConfig.getPassword());
        driver.findElement(By.id("chkloginpass")).click();
        driver.findElement(By.id("btnLogin2")).click();
        sleep(500);
        if (checkIntegral()) {
            log.info("今天观看课件已达到上限");
            driver.close();
            driver.quit();
            System.exit(0);
        }
        Long count = JDBCUtil.findCount("select count(id) from learning_progress where state = 0", null);
        if (count == null || count == 0) {
            clearData();
            initData();
        }
        StringBuilder knowledgeTypes = new StringBuilder();
        String[] knowledgeTypeArray = eduConfig.getKnowledgeTypes();
        for (String knowledgeType : knowledgeTypeArray) {
            knowledgeTypes.append("'");
            knowledgeTypes.append(knowledgeType);
            knowledgeTypes.append("'");
            knowledgeTypes.append(",");
        }
        knowledgeTypes.deleteCharAt(knowledgeTypes.length() - 1);
        String sql = "select * from learning_progress where state = 0" + (knowledgeTypes.toString().length() > 2 ? " and knowledge_type in (" + knowledgeTypes.toString() + ")" : "");
        List<LearningProgress> list = JDBCUtil.find(sql, null, LearningProgress.class);
        int learningCount = startLearning(list);
        log.info("今天实际学习{}课件", learningCount);
        sleep(1000);
        driver.close();
        driver.quit();
    }

}
